package com.ggb.reactive.controller.notify

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.ggb.reactive.constant.NotificationConstant
import com.ggb.reactive.constant.OrderPayStatus
import com.ggb.reactive.constant.OrderStatus
import com.ggb.reactive.handler.WebSocketHandler
import com.ggb.reactive.properties.WeChatProperties
import com.ggb.reactive.repository.OrderRepository
import com.ggb.reactive.service.OrderService
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil
import io.swagger.v3.oas.annotations.tags.Tag
import lombok.RequiredArgsConstructor
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.nio.charset.StandardCharsets
import java.time.Instant

@RestController
@RequestMapping("/notify")
@Tag(name = "支付回调相关接口")
@RequiredArgsConstructor
class PayNotifyController(
    val orderRepository: OrderRepository,
    val orderService: OrderService,
    val weChatProperties: WeChatProperties,
    val webSocketHandler: WebSocketHandler
) {
    /**
     * 支付成功回调
     */
    @RequestMapping("/paySuccess")
    fun paySuccessNotify(exchange: ServerWebExchange): Mono<Void> {
        //读取数据
        return exchange.request.body
            .map { buffer -> buffer.asInputStream().reader().use { it.readText() } }
            .flatMap { body ->
                println("支付成功回调：$body")

                //数据解密
                val plainText = decryptData(body)
                println("解密后的文本：$plainText")

                val jsonObject = JSON.parseObject(plainText)
                val outTradeNo = jsonObject.getString("out_trade_no")//商户平台订单号
                val transactionId = jsonObject.getString("transaction_id")//微信支付交易号

                println("商户平台订单号：$outTradeNo")
                println("微信支付交易号：$transactionId")

                //业务处理，修改订单状态、来单提醒
                orderRepository.findOneByNumber(outTradeNo).flatMap { order ->
                    order.status = OrderStatus.TO_BE_CONFIRMED.getStatus()
                    order.payStatus = OrderPayStatus.PAID.getStatus()
                    order.checkoutTime = Instant.now()
                    orderRepository.save(order).doOnSuccess {
                        val map = hashMapOf<String, Any>()
                        map["orderId"] = it.id!!
                        map["type"] = NotificationConstant.NEW_ORDER.getStatus()
                        map["content"] = "订单号：$outTradeNo"
                        val json = JSONObject(map).toString()
                        webSocketHandler.sendToAllClient(json)
                    }
                }
            }
            .then(responseToWeixin(exchange))
    }


    /**
     * 数据解密
     */
    private fun decryptData(body: String?): String {
        val resultObject = JSON.parseObject(body)
        val resource = resultObject.getJSONObject("resource")
        val ciphertext = resource.getString("ciphertext")
        val nonce = resource.getString("nonce")
        val associatedData = resource.getString("associated_data")

        val aesUtil = AesUtil(weChatProperties.apiV3Key!!.toByteArray(StandardCharsets.UTF_8))
        //密文解密
        return aesUtil.decryptToString(
            associatedData.toByteArray(StandardCharsets.UTF_8),
            nonce.toByteArray(StandardCharsets.UTF_8),
            ciphertext
        )
    }

    /**
     * 给微信响应
     */
    private fun responseToWeixin(exchange: ServerWebExchange): Mono<Void> {
        val response = exchange.response
        response.statusCode = HttpStatus.valueOf(200)
        val map = mapOf("code" to "SUCCESS", "message" to "SUCCESS")
        response.headers.contentType = MediaType.APPLICATION_JSON
        return response.writeWith(
            Mono.just(
                response.bufferFactory().wrap(JSON.toJSONString(map).toByteArray(StandardCharsets.UTF_8))
            )
        )
    }

}